package cn.zkdcloud.framework.handler;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import cn.zkdcloud.framework.annnotion.Before;
import cn.zkdcloud.framework.annnotion.Process;

import javax.servlet.http.HttpServletRequest;

import cn.zkdcloud.framework.config.Config;
import cn.zkdcloud.framework.servlet.HttpContext;
import cn.zkdcloud.framework.validation.Validation;

public class RequestServletHandler extends AbstractHandler {



	private Map<String,Class> urlMapping;

	/**init
	 *
	 */
	public RequestServletHandler(){
		this.urlMapping = Config.getUrlMapping();
	}

	/**dispatcher
	 *
	 * @param httpContext applicationContext
	 */
	public void handler(HttpContext httpContext,Iterator<Handler> it){
		this.request = httpContext.getRequest();
		this.response = httpContext.getResponse();
		String url = request.getRequestURI();

		try{
			handlerUrl(url,httpContext);

		}catch(Exception e){
			try {
				httpContext.getResponse().sendError(404,"this request is not find");
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}

	}
	/** find specify url
	 *
	 * @param url target url
	 * @param httpContext applicationContext
	 */
	public void handlerUrl(String url,HttpContext httpContext) {
		Set<String> keys = urlMapping.keySet();
		for(String urlKey : keys){
			if(url.startsWith(urlKey)){
				try{
					handlerRequest(urlKey, urlMapping.get(urlKey),httpContext);
					return ;
				}catch(RuntimeException e){
					continue;
				}
			}
		}
		throw new RuntimeException("this request is not find");
	}

	/** invoke method
	 *
	 */
	public void handlerRequest(String clazzUrl,Class clazz,HttpContext httpContext){
		try{
			HttpServletRequest request = httpContext.getRequest();
			Map<String,Method> methodUrls = getMethodUrlByClass(clazz);
			String queryUrl = httpContext.getRequest().getRequestURI().substring(clazzUrl.length()-1);

			Set<String> methodKeyUrls = methodUrls.keySet();
			for(String methodUrl : methodKeyUrls){
				if(queryUrl.startsWith(methodUrl)){
					Method method = methodUrls.get(methodUrl);
					Process process = method.getAnnotationsByType(Process.class)[0]; // later treatment
					if(process.method().toString().equalsIgnoreCase(request.getMethod())){
						if(doBefore(method,httpContext)){ //the validation before into method
							method.invoke(clazz.newInstance(), httpContext);
							return;
						}
					}
				}
			}
		}catch(Exception e){
			e.printStackTrace();
		}

		if(httpContext.getRender() == null){
			throw new RuntimeException("this request is not this class");
		}

	}
	/** get Map(url,method) by class
	 *
	 * @param clazz class
	 * @return map
	 */
	public Map<String,Method> getMethodUrlByClass(Class clazz){
		Map<String,Method> methodUrls = new HashMap<String,Method>();
		Method[] methods = clazz.getDeclaredMethods();
		for(Method method : methods){
			Process[] process = method.getAnnotationsByType(Process.class);
			if(process.length != 0){
				methodUrls.put(process[0].value(), method);
			}
		}
		return methodUrls;
	}

	/** the validation before into method
	 *
	 */
	public boolean doBefore(Method method,HttpContext httpContext){
		try{
			Before[] befores = method.getAnnotationsByType(Before.class); //later zai chu li...
			if(befores == null){
				return true;
			}
			Class<? extends Validation>[] validationClazzs = befores[0].values();
			for(Class valiClazz : validationClazzs){
				Validation validation  = (Validation) valiClazz.newInstance();
				return validation.validate(httpContext);
			}
		}catch (Exception e){
			return true;
		}
		return true;
	}
}
